package xddq

import (
	"context"
	"fmt"
	"runtime/debug"
	"sync"
	"time"
	D "websocket_apn/define"
	"websocket_apn/log"
	"websocket_apn/proto"
	"websocket_apn/transfer"
	ws "websocket_apn/transfer/net/websocket"
)

type XdDriver struct {
	userInfo      D.UserInfo
	UserData      D.UserGameData
	Transfer      transfer.Transfer
	lastSendTime  time.Time
	pingTime      int  //ping的周期 最大30秒断线
	Debug         bool //是否是调试模式
	mutex         sync.Mutex
	StartSendCmd  bool       //是否可以发送命令
	SwitchCloneMu sync.Mutex //切换分身锁
	ctx           context.Context
	cancel        context.CancelFunc
}

// NewHTTPTransfer 新建通讯器
func NewXdDriver(param Param) (*XdDriver, error) {
	if len(param.UserInfo.WsAddress) < 6 {
		param.UserInfo.WsAddress = fmt.Sprintf("wss://proxy-xddq.hdnd01.com/s%d_ws", param.UserInfo.PlayerId/1000000)
	}
	xd := &XdDriver{
		userInfo: param.UserInfo,
		Debug:    false, //是否是调试模式
	}
	xd.ctx, xd.cancel = context.WithCancel(context.Background())
	xd.Init()
	return xd, nil
}

func (xd *XdDriver) Init() {
	xd.pingTime = 20
	xd.lastSendTime = time.Now().Add(-time.Duration(xd.pingTime) * time.Second)
	xd.UserData = D.UserGameData{
		UserInfo: xd.userInfo,
		UserID:   "E28386B8C05A",
	}
	xd.FuDiDataInit()
	xd.KanShuDataInit()
	xd.FightInit()
	xd.XddTaskInit()
	if _, ok := D.UserGameDataMap[xd.userInfo.PlayerId]; !ok {
		D.UserGameDataMap[xd.userInfo.PlayerId] = &xd.UserData
	}

	transfer, err := ws.NewWebTransfer(ws.Param{WebServer: xd.userInfo.WsAddress})
	if err != nil {
		log.Error("NewWebTransfer fail: ", err)
	}
	xd.Transfer = transfer
}

// Send 发送
func (xd *XdDriver) Send(sendData *D.Message) D.ErrorCode {
	xd.mutex.Lock()
	defer xd.mutex.Unlock()
	if sendData == nil {
		return D.Success
	}
	//调试模式只打印发送数据
	if xd.Debug {
		if len(sendData.PayloadBytes) > 0 {
			log.Infof("web send[%4d]: %s", len(sendData.PayloadBytes), proto.HexToHexStr(sendData.PayloadBytes))
		}
		return D.Success
	}
	if xd.Transfer.Send(sendData) != D.Success {
		xd.cancel()
		return D.ErrorTransferSendError
	}
	//xd.lastSendTime = time.Now()
	return D.Success
}

func (xd *XdDriver) Receive(length int, msgId int32) (D.ErrorCode, *D.Message) {
	// 设置超时
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	// 创建一个通道来接收返回结果
	resultChan := make(chan *D.Message)
	// 创建一个接收消息的 goroutine
	go func() {
		errCode, resData := xd.Transfer.Receive(0, 0)
		if errCode != D.Success {
			log.Debugf("recv data fail")
			return
		}
		resultChan <- resData
	}()

	// 等待超时或接收到消息
	select {
	case <-ctx.Done():
		if ctx.Err() == context.DeadlineExceeded {
			return D.ErrorTimeOut, nil
		}
	case result := <-resultChan:
		return D.Success, result
	}
	return D.ErrorUnknown, nil
}

func (xd *XdDriver) startPingProcess() {
	log.Debug("start web ping thread.")
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("process:\n%s", debug.Stack())
			xd.Close()
			time.Sleep(time.Second)
		}
	}()

	for {
		select {
		case <-xd.ctx.Done():
			{
				log.Warnf("process thread exit.")
				return
			}
		case <-time.After(time.Second * 15):
			if time.Now().Sub(xd.lastSendTime) < (time.Duration(xd.pingTime) * time.Second) {
				continue
			}
			log.Debugf("start send ping")
			sendData := proto.Get_Cmd_No_Data(xd.userInfo.PlayerId, proto.S_PLAYER_PING)
			if xd.Send(sendData) != D.Success {
				log.Error("send ping fail")
				return
			}
		}
	}
}

// Receive 接收
func (xd *XdDriver) StartRecvThread() {
	log.Debug("start web recv thread.")
	defer func() {
		if err := recover(); err != nil {
			log.Fatalf("process:\n%s", debug.Stack())
			xd.Close()
			time.Sleep(time.Second)
		}
	}()
	for {
		select {
		case <-xd.ctx.Done():
			{
				log.Warnf("StartRecvThread exit.")
				return
			}
		case <-time.After(50 * time.Millisecond):
			errCode, resData := xd.Transfer.Receive(0, 0)
			if errCode != D.Success {
				log.Debug("recv data fail")
				return
			}
			msg, _ := proto.DecodeMsgData(resData.PayloadBytes)
			if msg != nil {
				flag := true
				cmd := msg.Cmd
				if !xd.StartSendCmd && cmd == proto.S_PLAYER_PING_RSP {
					log.Infof("recv ping RSP")
					xd.StartSendCmd = true
				}
				if cmd == proto.S_HOMELAND_ENTER_RSP || cmd == proto.S_PLAYER_PING_RSP || cmd == 1001 || cmd == 1002 || cmd == 1201 ||
					cmd == proto.S_STAGE_VIEW_MONSTER_ATTR_RSP || cmd == proto.S_TOWER_VIEW_MONSTER_ATTR_RSP {
					flag = false
				}
				if flag {
					log.Debugf("↓↓↓↓↓ %s - len:%d", proto.GetGameCmdDesc(msg.Cmd), len(resData.PayloadBytes))
					for idx, data := range msg.JsData {
						log.Debugf("\t>>> %d - code %02X[%d] len:%d-%s_%+v\n", idx+1, data.Code, data.Code, len(data.HexData), proto.HexToHexStr(data.HexData), string(data.Data))
					}
				}
			}
			go xd.DealServerMsg(msg)
		}
	}
}

// Close 关闭
func (xd *XdDriver) Close() {
	xd.Transfer.Close()
}

// 等待获取活动任务ID
func (xd *XdDriver) waitForStartSendCmd() error {
	timeCount := 0
	log.Debug("wait for send cmd.")
	for !xd.StartSendCmd && timeCount < 1200 {
		select {
		case <-xd.ctx.Done(): // 检查上下文是否被取消
			return fmt.Errorf("operation canceled: %w", xd.ctx.Err())
		default:
			time.Sleep(time.Second)
			timeCount++
		}
	}
	return nil
}

// 判断是否是管理员账号
func (xd *XdDriver) checkIsAdminUserID(ID string) bool {
	if ID == "E28386B8C05A" {
		return true
	} else if ID == "EFE0D0C4C05A" {
		return true
	}
	return false
}

// 判断是否是管理员账号
func (xd *XdDriver) checkIsAdminCount() bool {
	if xd.Debug {
		return true
	}
	if xd.userInfo.PlayerId == 3109673861602 {
		return true
	} else if xd.userInfo.PlayerId == 3109700251759 {
		return true
	}
	return false
}

// Receive 接收
func (xd *XdDriver) StartXddqProgress() D.ErrorCode {
	if err := xd.LoginGameByWebSocket(); err != D.Success {
		return err
	}
	go xd.startPingProcess()
	go xd.StartRecvThread()
	if err := xd.waitForStartSendCmd(); err != nil {
		return D.ErrorTimeOut
	}
	//for i := 1; i < 21; i++ {
	//	xd.Send(proto.Get_Cmd_Data(xd.userInfo.PlayerId, 231202, fmt.Sprintf("08 AD FC 98 05 10 %02X 18 01 22 07 08 9F 18 10 01 18 03 22 07 08 87 20 10 01 18 03 22 07 08 B7 10 10 01 18 02 22 07 08 86 20 10 01 18 03 22 07 08 B6 10 10 01 18 01 22 07 08 CE 21 10 03 18 01 22 07 08 9E 18 10 01 18 02 22 07 08 E5 19 10 03 18 01 22 07 08 85 20 10 01 18 02 22 07 08 FE 11 10 03 18 01 22 07 08 9D 18 10 01 18 03", i)))
	//}
	go xd.StartXddTask()
	go xd.StartFuDiTask()
	go xd.StartKanShuTask()
	go xd.StartFightTask()
	return D.Success
}

// 登录游戏
func (xd *XdDriver) LoginGameByWebSocket() D.ErrorCode {
	xd.Send(proto.Get_S_PLAYER_LOGIN_data(xd.userInfo.PlayerId))
	if err, _ := xd.Receive(0, 0); err == D.ErrorTimeOut {
		return err
	}

	xd.Send(proto.Get_Cmd_Data(xd.userInfo.PlayerId, proto.S_CUT_PRICE_SYNC, ""))                                                                                                                                                                                                                                                                //
	xd.Send(proto.Get_Cmd_Data(xd.userInfo.PlayerId, proto.S_SET_MESSAGE_SUBSCRIBE, "0A 31 0A 2B 47 5A 31 39 6F 59 41 51 65 58 5A 6B 2D 38 62 63 75 61 6F 4C 6B 6A 78 5A 4D 36 50 56 73 71 64 57 4C 52 50 72 4C 53 47 4C 6A 46 77 10 00 18 01 10 01 1A 1C 6F 46 2D 42 6D 35 46 49 34 69 48 59 4E 58 4F 62 35 47 74 4B 37 6B 54 45 37 73 47 67")) //
	time.Sleep(2 * time.Second)
	//登录
	xd.Send(proto.Get_Cmd_Data(xd.userInfo.PlayerId, proto.S_GET_BIND_CODE_INFO_REQ, "0A 07 33 37 77 78 78 79 78 10 D9 01")) //
	time.Sleep(2 * time.Second)
	xd.Send(proto.Get_Cmd_Data(xd.userInfo.PlayerId, proto.S_GET_TOP_PLAYER_INFO, ""))    //
	xd.Send(proto.Get_Cmd_Data(xd.userInfo.PlayerId, proto.SYNC_UNION_DUEL_MSG, "08 01")) //
	return D.Success
}

func StartGame(userInfo D.UserInfo) D.ErrorCode {
	log.Debugf("userInfo %+v", userInfo)
	xd, err := NewXdDriver(Param{UserInfo: userInfo})
	if err != nil {
		log.Error("NewWebTransfer fail: ", err)
	}
	//接收参数转发

	if err := xd.StartXddqProgress(); err != D.Success {
		return err
	}

	<-xd.ctx.Done()
	log.Warnf("玩家【%s】游戏异常退出", userInfo.NickName)
	xd.Close()
	return D.ErrorUnknown
}
